/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.aider.web.listener;

import cn.hutool.core.collection.CollUtil;
import com.iwindplus.aider.web.domain.property.AiderWebProperty;
import com.iwindplus.aider.web.domain.property.AiderWebProperty.ServerApiConfig;
import com.iwindplus.base.domain.vo.ResultVO;
import com.iwindplus.base.domain.vo.ServerInfoVO;
import com.iwindplus.base.swagger.service.SwaggerService;
import com.iwindplus.mgt.client.system.ServerApiClient;
import com.iwindplus.mgt.domain.constant.MgtConstant;
import jakarta.annotation.Resource;
import java.time.Duration;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.event.EventListener;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

/**
 * 服务API注册监听器.
 *
 * @author zengdegui
 * @since 2023/07/30 22:31
 */
@Slf4j
public class ServerApiRegisterListener {

    @Value("${spring.application.name}")
    private String applicationName;

    @Resource
    private DiscoveryClient discoveryClient;

    @Resource
    private AiderWebProperty property;

    @Resource
    private SwaggerService swaggerService;

    @Resource
    private ServerApiClient serverApiClient;

    /**
     * 启动完成后异步注册，主线程立即返回
     */
    @EventListener(ApplicationReadyEvent.class)
    public void onAppReady() {
        ServerApiConfig cfg = property.getServerApi();
        if (!cfg.getEnabled()) {
            log.info("服务API注册已关闭");
            return;
        }

        registerApiReactive(cfg);
    }

    /**
     * 纯 Reactor 非阻塞注册流程
     */
    private Disposable registerApiReactive(ServerApiConfig cfg) {
        long interval = cfg.getRetryInterval();
        int maxRetry = cfg.getMaxRetry();

        return Flux.interval(Duration.ZERO, Duration.ofSeconds(interval))
            .take(maxRetry)
            .doOnNext(i -> log.info("服务API注册轮询，第 {} 次", i + 1))
            .concatMap(ignore -> discoverOnce())
            .filter(CollUtil::isNotEmpty)
            .next()
            .doOnNext(instances -> log.info("已发现 {} 服务实例，准备注册", MgtConstant.MGT_SERVER_NAME))
            .publishOn(Schedulers.boundedElastic())
            .map(instances -> realRegister())
            .filter(Boolean::booleanValue)
            .single()
            .doOnSuccess(instances -> log.info("{} 服务API注册成功", applicationName))
            .onErrorResume(NoSuchElementException.class, e -> {
                String msg = "服务API注册失败，已达最大重试次数：" + maxRetry;
                return Mono.error(new RuntimeException(msg));
            })
            .subscribe(
                data -> log.debug("onNext: {}", data),
                err -> log.error("{}", err.getMessage()),
                () -> log.debug("onComplete")
            );
    }

    /**
     * 单次发现服务 —— 防止 DiscoveryClient 实现阻塞，依旧放到 boundedElastic
     */
    private Mono<List<ServiceInstance>> discoverOnce() {
        return Mono.fromCallable(() ->
                discoveryClient.getInstances(MgtConstant.MGT_SERVER_NAME))
            .subscribeOn(Schedulers.boundedElastic());
    }

    /**
     * 真正的注册，可能包含远程调用，允许阻塞线程池
     */
    private boolean realRegister() {
        final ServerInfoVO entity = swaggerService.getServerInfo();
        if (Objects.nonNull(entity)) {
            final ResultVO<Boolean> result = serverApiClient.saveOrEdit(entity);
            result.errorThrow();
            return result.getBizData();
        }
        return false;
    }
}
